/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package pl.edu.amu.wmi.kino.visualjavafx.visualjavafxgui.editor.visual;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.swing.JComponent;
import org.netbeans.api.visual.model.ObjectScene;
import org.netbeans.api.visual.widget.LayerWidget;
import org.netbeans.api.visual.widget.Widget;
import org.openide.util.Lookup;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;
import pl.edu.amu.wmi.kino.visualjavafx.model.api.ModelChangeEvent;
import pl.edu.amu.wmi.kino.visualjavafx.model.api.ModelChangeListener;
import pl.edu.amu.wmi.kino.visualjavafx.model.api.ModelChangeNotifier;
import pl.edu.amu.wmi.kino.visualjavafx.model.objects.Canvas;
import pl.edu.amu.wmi.kino.visualjavafx.model.objects.ContentContainer;
import pl.edu.amu.wmi.kino.visualjavafx.model.objects.Frame;
import pl.edu.amu.wmi.kino.visualjavafx.model.objects.VisualObject;
import pl.edu.amu.wmi.kino.visualjavafx.visualjavafxgui.editor.visual.widgets.VisualObjectWidget.Synchronizer;
import pl.edu.amu.wmi.kino.visualjavafx.visualjavafxgui.editor.visual.widgets.factory.api.WidgetFactory;

/**
 *
 * @author psychollek
 */
public class VisualScene extends ObjectScene{

    private InstanceContent ic = new InstanceContent();
    private Lookup lookup = new AbstractLookup(ic);
    private LayerWidget visualObjects = new LayerWidget(this);
    private LayerWidget background = new LayerWidget(this);

    public VisualScene(){
        addChild(this.background);
        addChild(this.visualObjects);
        ic.add(new WidgetCreator(this));
        ic.add(new SceneNotifier());
    }

    @Override
    public JComponent getView(){
        return this.createView();
    }

    @Override
    public Lookup getLookup(){
        return lookup;
    }

    public class WidgetCreator{

        private VisualScene s = null;
        private Collection<? extends WidgetFactory> factories = Lookup.getDefault().lookupAll(WidgetFactory.class);
        private Frame frm = null;


        private WidgetCreator(VisualScene s) {
            this.s = s;
        }

        public void setModel(Frame frm){
            this.addObject(frm);
            revalidate();
        }

        //TODO :Adam: BUG! validation now is ok, but null pointer exception is still here - wrong repair ?
        //TODO :Adam: BUG! adding new object to model dosen't update the nodes until they are created again !
        public void addNew(VisualObject vo) {
            if (frm != null){
                if (vo instanceof Canvas){
                    //don't know what to do here ...
                }else{
                    frm.getCanvas().getContent().add(vo);
                    addObject(vo,visualObjects);
                    s.lookup.lookup(SceneNotifier.class).newChildAdded(vo, frm.getCanvas());
                }
            }
            revalidate();
            repaint();
        }

        private void addObject(Canvas c){
            ArrayList<VisualObject> arr = c.getContent();
            for (Iterator<VisualObject> it = arr.iterator(); it.hasNext();) {
                VisualObject vo = it.next();
                this.addObject(vo, visualObjects);
            }
        }

        //TODO :Adam: remember that it might be a source of roblems ! test it.
        private void addObject(ContentContainer cc, Widget par){
            java.awt.Toolkit.getDefaultToolkit().beep();
            Widget w = widget(cc);
            s.addObject(cc, w);
            par.addChild(w);
            ArrayList<VisualObject> arr = cc.getContent();
            for (Iterator<VisualObject> it = arr.iterator(); it.hasNext();) {
                VisualObject vo = it.next();
                this.addObject(vo, w);
            }
        }

        private void addObject(VisualObject o, Widget par){
            Widget w = widget(o);
            s.addObject(o, w);
            par.addChild(w);
        }

        private void addObject(Frame frm){
            this.frm = frm;
            Widget w = widget(frm);
            s.addObject(frm.getCanvas(), w);
            background.addChild(w);
            this.addObject(frm.getCanvas());
        }


        private Widget widget(Object o){
            Widget w = null;
            for (Iterator<? extends WidgetFactory> it = factories.iterator(); it.hasNext();) {
                WidgetFactory widgetFactory = it.next();
                if (widgetFactory.canGenerateFrom(o)){
                    w = widgetFactory.generate(s, o);
                    w.getActions().addAction(0,s.createSelectAction());
                    return w;
                }
            }
            return null;
        }

    }

    public class SceneNotifier implements ModelChangeListener,ModelChangeNotifier{

        private ArrayList<ModelChangeListener> listeners = new ArrayList<ModelChangeListener>();

        private SceneNotifier(){

        };

        private void newChildAdded(VisualObject vo, Object par){
            ModelChangeEvent evt = ModelChangeEvent.newChildren(this, par, vo);
            for (Iterator<ModelChangeListener> it = listeners.iterator(); it.hasNext();) {
                ModelChangeListener mcl = it.next();
                mcl.notifyModelChange(evt);
            }
        }

        private void modelToWidget(Object o){
            Synchronizer sync = null;
            if (o instanceof Frame){
                sync = findWidget(((Frame) o).getCanvas()).getLookup().lookup(Synchronizer.class);
            }else{
                sync = findWidget(o).getLookup().lookup(Synchronizer.class);
            }
            if (sync != null){
                sync.modelToWidget();
            }
            repaint();
        }

        @Override
        public void addModelChangeListener(ModelChangeListener l){
            listeners.add(l);
        }

        @Override
        public void notifyModelChange(ModelChangeEvent evt) {
            if (evt.getDescription().startsWith("propertyChanged: ")){
                modelToWidget(evt.getModifiedElement());
            }else if ( (evt.getWho()[0] instanceof Widget)
                       && (findObject((Widget)evt.getWho()[0]) != null)){
                Object[] who = evt.getWho();

                for (int i = 0; i < who.length; i++) {
                    if (who[i] == this) return;
                }

                for (Iterator<ModelChangeListener> it = listeners.iterator(); it.hasNext();) {
                    ModelChangeListener modelChangeListener = it.next();
                    modelChangeListener.notifyModelChange(ModelChangeEvent.passEvent(evt, this));
                }
            }
        }

        @Override
        public void removeModelChangeListener(ModelChangeListener listener) {
            listeners.remove(listener);
        }

    }

}




